# encoding: utf-8
import calendar
import datetime
import httplib

import caldav
import dateutil.relativedelta
import urwid

DAYABBRS = [_(u'Mon'), _(u'Tue'), _(u'Wed'), _(u'Thu'), _(u'Fri'), _(u'Sat'),
            _(u'Sun')]


class MainFrame(urwid.Frame):

    VIMMAPPING = {'k': 'up', 'j': 'down', 'h': 'left', 'l': 'right'}

    def __init__(self, config):
        self.config = config
        self.calendars = {}
        self.base = datetime.date.today()

        # widgets
        self.calendar = CalendarWidget(self)
        self.calendar_list = CalendarListWidget(self)
        self.dailyview = DailyWidget(self)
        body = urwid.ListBox([urwid.Columns([self.dailyview,
                                             urwid.Pile([self.calendar,
                                                         self.calendar_list]),
                                            ])])

        header = urwid.AttrWrap(urwid.Text('Calmut', align='center'), 'header')

        super(MainFrame, self).__init__(body, header=header)

    def on_calendar_changed(self, widget, state):
        if state:
            client = caldav.DAVClient(widget.url)
            calendar = caldav.objects.Calendar(client, widget.url)
            self.calendars[widget.calname] = calendar
        else:
            del self.calendars[widget.calname]

    def keypress(self, size, key):
        if key == 'q':
            raise urwid.ExitMainLoop()
        elif key in self.VIMMAPPING:
            return super(MainFrame, self).keypress(size, self.VIMMAPPING[key])
        else:
            return super(MainFrame, self).keypress(size, key)


class CalendarWidget(urwid.WidgetWrap):

    def __init__(self, parent):
        self.parent = parent
        self.calendar = calendar.TextCalendar()
        self.update_calendar()
        super(CalendarWidget, self).__init__(self._w)

    def update_calendar(self):
        today = datetime.date.today()
        dates = self.calendar.itermonthdates(self.parent.base.year,
                                             self.parent.base.month)
        dates_widget = [[] for x in range(7)]
        for idx, name in enumerate(DAYABBRS):
            dates_widget[idx % 7].append(urwid.AttrWrap(urwid.Text(name),
                                                        'day_name'))
        for idx, date in enumerate(dates):
            day_widget = DateIcon('%s' % date.day, self.on_date_clicked)
            day_widget.date = date
            if date.month != self.parent.base.month:
                day_widget = urwid.AttrWrap(day_widget, 'other_day')
            elif date == today:
                day_widget = urwid.AttrWrap(day_widget, 'today')
            elif date == self.parent.base:
                day_widget = urwid.AttrWrap(day_widget, 'selected')
            else:
                day_widget = urwid.AttrWrap(day_widget, 'month_day')
            dates_widget[idx % 7].append(day_widget)
        calendar_box = urwid.Columns([urwid.Pile(weekdays)
                                      for weekdays in dates_widget])
        month_box = urwid.Text(self.parent.base.strftime('%Y ─ %m'),
                               align='center')

        self._w = urwid.LineBox(urwid.Pile([month_box, urwid.Divider("─"),
                                            calendar_box]))

    def keypress(self, size, key):
        if key in '<t>':
            delta = dateutil.relativedelta.relativedelta(months=1)
            if key == '<':
                self.parent.base -= delta
            elif key == '>':
                self.parent.base += delta
            elif key == 't':
                self.parent.base = datetime.date.today()
            self.update_calendar()
        else:
            return super(CalendarWidget, self).keypress(size, key)

    def on_date_clicked(self, button):
        self.parent.base = button.date
        self.update_calendar()
        self.parent.dailyview.update_view()


class DateIcon(urwid.SelectableIcon):

    signals = ['click']

    def __init__(self, text, callback=None, data=None):
        super(DateIcon, self).__init__(text)
        if callback is not None:
            urwid.connect_signal(self, 'click', callback, data)

    def keypress(self, size, key):
        if urwid.command_map[key] != 'activate':
            return key
        self._emit('click')


class CalendarListWidget(urwid.WidgetWrap):

    def __init__(self, parent):
        self.parent = parent
        self.create_list()
        super(CalendarListWidget, self).__init__(self.widget)

    def create_list(self):
        config = self.parent.config
        calendars = [section for section in config.sections()
                     if section.startswith('calendar')]
        calendars.sort()
        widgets = []
        for calendar in calendars:
            calendar_name = config.get(calendar, 'name')
            callback = self.parent.on_calendar_changed
            checkbox = urwid.CheckBox((calendar, calendar_name),
                                      on_state_change=callback)
            checkbox.url = self.parent.config.get(calendar, 'url')
            checkbox.calname = calendar
            checkbox.set_state(config.getboolean(calendar, 'activated'))
            widgets.append(checkbox)
        if widgets:
            self.widget = urwid.Pile(widgets)
        else:
            txt = 'You should define calendars in $HOME/.calmut'
            self.widget = urwid.Text(txt)


class DailyWidget(urwid.WidgetWrap):

    def __init__(self, parent):
        self.parent = parent
        self.update_view()
        super(DailyWidget, self).__init__(self._w)

    def update_view(self):
        widgets = [urwid.Text(self.parent.base.strftime('%Y-%m-%d'),
                              align='center'),
                   urwid.Divider("─")]

        base_events = []
        for calname, calendar in self.parent.calendars.items():
            # Some servers drops the connection and we receive a BadStatusLine
            try:
                dates = calendar.date_search(self.parent.base)
            except httplib.BadStatusLine:
                url = calendar.url.geturl()
                client = caldav.DAVClient(url)
                calendar = caldav.objects.Calendar(client, url)
                self.parent.calendars[calname] = calendar
                dates = calendar.date_search(self.parent.base)

            for event in dates:
                event.load()
                event_date = event.instance.vevent.dtstart.value
                if isinstance(event_date, datetime.datetime):
                    event_date = event_date.date()
                if event_date == self.parent.base:
                    base_events.append((event_date, calname,
                                        event.instance.vevent))

        for date, calname, event in sorted(base_events):
            start = event.dtstart.value.strftime('%H:%M')
            end = event.dtend.value
            if end.date() == self.parent.base:
                end = end.strftime('%H:%M')
            else:
                end = end.strftime('%Y-%m-%d %H:%M')
            event_txt  = '%s -> %s : %s' % (start, end, event.summary.value)
            widgets.append(urwid.AttrWrap(urwid.Text(event_txt), calname))

        self._w = urwid.Pile(widgets)
